#include "StdAfx.h"
#include "Tools.h"

namespace Tools
{
	bool check_border_condition(int** mask)
	{
		int sum = 0;
		int product = 0;
		int ordered_set[8] = {mask[0][1],mask[0][2],mask[1][2],mask[2][2],mask[2][1],mask[2][0],mask[1][0],mask[0][0]};
		for (int i = 0; i< 8; i++)
		{
			product*= ordered_set[i];
			sum+= ordered_set[i];
		}
		if (sum > 0)
		{
			if (mask[0][1] * mask[1][2] * mask[2][1] * mask[1][0] == 0)
			{
				return true;
			}
		}
		return false;
	}

	set<CvPoint,ImagePointComp> marking_borders(IplImage *source)
	{
		set<CvPoint,ImagePointComp> borders;
		borders.clear();
		int** mask = new int * [3];
		for (int k = -1; k < 2; k++)
		{
			mask[k+1] = new int [3];
		}

		for (int y = 0; y < source->height; y++)
		{
			for (int x = 0; x < source->width; x++)
			{
				// Creating masks for image pixel
				if (((uchar *)(source->imageData + y * source->widthStep))[x] == 0)
				{
					int neighbourhood_weight = 0;
					for (int k = -1; k < 2; k++)
					{
						for (int l = -1; l < 2; l++)
						{
							if ((y + k < 0) || (x + l < 0) || (y + k >= source->height) || (x + l >= source->width) )
							{
								mask[k+1][l+1] = 0;
							}
							else
							{
								uchar pixel_value = (((uchar *)(source->imageData + (y + k) * source->widthStep))[x + l] == 255) ? 0 : 1;
								mask[k+1][l+1] = pixel_value;
							}
						}
					}
			
					if (check_border_condition(mask))
					{
						CvPoint pi;
						pi.x = x;
						pi.y = y;
						borders.insert(pi);
					}
				}
			
			}
		}

		// Delete mask
		for (int k = 0; k < 3; k++)
		{
			delete [] mask[k];
		}
		delete [] mask;
		return borders;
	}


	double distance_Euclidean(CvPoint p1, CvPoint p2)
	{
		return sqrt((double)((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)));
	}

	double distance_point_to_line(CvPoint p0, CvPoint v0, CvPoint v1)
	{
		double a = distance_Euclidean(p0,v0);
		double b = distance_Euclidean(p0,v1);
		double c = distance_Euclidean(v0,v1);

		double p = (a + b + c) / 2;

		// Calculate area of triangle by Heron's formula
		double h = 2 * (sqrt(p * (p - a) * (p - b) * (p - c))) / c;

		return h;
	}

	set<CvPoint,ImagePointComp> get_substroke_elements(CvPoint first_point, IplImage* map, CvScalar color, CvScalar &combine_color)
	{
		set<CvPoint,ImagePointComp> retval;
		list<CvPoint> next_points;
		next_points.clear();
		next_points.push_back(first_point);
		bool iscombined = false;
		
		while(!next_points.empty())
		{
			CvPoint current_point = next_points.front();

			// Generate neighbour points
			for (int i = -1; i< 2; i++)
			{
				for (int j = -1; j< 2; j++)
				{
					if( (i + j == -1) || (i + j == 1))
					{
						CvPoint neighbour_point;
						neighbour_point.x = current_point.x + j;
						neighbour_point.y = current_point.y + i;
						if ((neighbour_point.x < 0) || (neighbour_point.x >= map->width) || (neighbour_point.y < 0) || (neighbour_point.y >= map->height))
						{
							continue;
						}
					
						else
						{
							CvScalar pixel_color;
							pixel_color = cvGet2D(map,neighbour_point.y, neighbour_point.x);
							if ((pixel_color.val[0] == color.val[0]) && (pixel_color.val[1] == color.val[1]) && (pixel_color.val[2] == color.val[2]))
							{
								list<CvPoint>::iterator it;
								bool is_contained_in_list = false;
								// Check if generated points are in list or in result set
								// If yes, ignore them
								if (retval.find(neighbour_point) != retval.end())
								{
									continue;
								}
								for (it = next_points.begin(); it != next_points.end(); it++)
								{

									if ((neighbour_point.x == (*it).x) && (neighbour_point.y == (*it).y))
									{
										is_contained_in_list = true;
										break;
									}

								}
								if (is_contained_in_list)
								{
									continue;
								}

								next_points.push_back(neighbour_point);

							}
							else if (((pixel_color.val[0] > 0) && (pixel_color.val[0] < 255)) ||((pixel_color.val[1] > 0) && (pixel_color.val[1] < 255)) || ((pixel_color.val[2] > 0) && (pixel_color.val[2] < 255)))
							{
								combine_color = pixel_color;
							}
						}
					}
				}
			}
			// Put current point in the result set
			// And then, pop it from list
			retval.insert(current_point);
			next_points.pop_front();
		}
		return retval;
	}

	bool point_compare (const CvPoint & lhs, const CvPoint & rhs)
	{
			if (lhs.y < rhs.y)
			{
				return true;
			}
			else if (lhs.y > rhs.y)
			{
				return false;
			}
			else if (lhs.x < rhs.x)
			{
				return true;
			}
			else
			{
				return false;
			}
	}

	vector<vector<CvPoint>> create_segments(vector<CvPoint> points)
	{
		vector<vector<CvPoint>> retval;
		while(!points.empty())
		{
			vector<CvPoint> segment;
			stack<CvPoint> temp;
			temp.push(points.front());
			points.erase(points.begin());
			// Find neighbour points
			while(!temp.empty())
			{
				CvPoint element = temp.top();
				segment.push_back(element);
				temp.pop();
				for (int i = -1; i< 2; i++)
				{
					for (int j = -1; j< 2; j++)
					{
						CvPoint p;
						p.x = element.x + j;
						p.y = element.y + i;
						int pos = 0;
						bool found = false;
						for (pos = 0; pos < (int) points.size(); pos++)
						{
							if ((p.x == points[pos].x) && (p.y == points[pos].y))
							{
								found = true;
								break;
							}

						}
						if (found)
						{
							temp.push(points[pos]);
							points.erase(points.begin() + pos);
						}
					}
				}

			}
			sort(segment.begin(),segment.end(),point_compare);
			retval.push_back(segment);
		}

		return retval;
	}

	void draw_stroke(const char* filename, const vector<StrokeNode*> &nodes)
	{
		// Find bounding box
		int x1 = 100000;
		int y1 = 100000;
		int x2 = 0;
		int y2 = 0;
		
		for (int i = 0; i< (int) nodes.size(); i++)
		{
			set<CvPoint,ImagePointComp>::iterator it;
			for (it = nodes[i]->elements.begin(); it != nodes[i]->elements.end(); it++)
			{
				if (x1 > (*it).x)
				{
					x1 = (*it).x;
				}
				if (y1 > (*it).y)
				{
					y1 = (*it).y;
				}
				if (x2 < (*it).x)
				{
					x2 = (*it).x;
				}
				if (y2 < (*it).y)
				{
					y2 = (*it).y;
				}
			}
		}

		IplImage * image = cvCreateImage(cvSize(x2 - x1 + 1,y2 - y1 + 1),IPL_DEPTH_8U,3);
		cvSet(image,cvScalar(255,255,255));
		for (int i = 0; i< (int) nodes.size(); i++)
		{
			set<CvPoint,ImagePointComp>::iterator it;
			CvScalar paint_color;
			if (nodes[i]->isAmbigousZone)
			{
				paint_color = cvScalar(0,0,255);
			}
			else
			{
				paint_color = cvScalar(0,0,0);
			}
			
			for (it = nodes[i]->elements.begin(); it != nodes[i]->elements.end(); it++)
			{
				CvPoint current_point = (*it);
				current_point.x = current_point.x - x1;
				current_point.y = current_point.y - y1;

				cvSet2D(image,current_point.y, current_point.x,paint_color);
			}
			
			cvSaveImage(filename,image);
		}
		cvReleaseImage(&image);
		return;
	}
}